package org.xq.softcup.mq.remoting.invoker.impl;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.util.ReflectionUtils;
import org.xq.softcup.mq.registry.ServiceRegistry;
import org.xq.softcup.mq.remoting.invoker.XQRpcInvokerFactory;
import org.xq.softcup.mq.remoting.invoker.annotation.XQRpcReference;
import org.xq.softcup.mq.remoting.invoker.reference.XQRpcReferenceBean;
import org.xq.softcup.mq.remoting.provider.XQRpcProviderFactory;
import org.xq.softcup.mq.util.XQRpcException;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Slf4j
public class XQRpcSpringInvokerFactory extends InstantiationAwareBeanPostProcessorAdapter implements InitializingBean, DisposableBean, BeanFactoryAware {
    private Class<? extends ServiceRegistry> serviceRegistryClass;
    private Map<String, String> serviceRegistryParam;
    private XQRpcInvokerFactory xqRpcInvokerFactory;
    private BeanFactory beanFactory;

    public void setServiceRegistryClass(Class<? extends ServiceRegistry> serviceRegistryClass) {
        this.serviceRegistryClass = serviceRegistryClass;
    }

    public void setServiceRegistryParam(Map<String, String> serviceRegistryParam) {
        this.serviceRegistryParam = serviceRegistryParam;
    }

    public void afterPropertiesSet()
            throws Exception {
        this.xqRpcInvokerFactory = new XQRpcInvokerFactory(this.serviceRegistryClass, this.serviceRegistryParam);
        this.xqRpcInvokerFactory.start();
    }

    public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException {
        final Set<String> serviceKeyList = new HashSet();

        ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                if (field.isAnnotationPresent(XQRpcReference.class)) {
                    Class iface = field.getType();
                    if (!iface.isInterface()) {
                        throw new XQRpcException("引用必须为interface接口.");
                    }
                    XQRpcReference rpcReference = (XQRpcReference) field.getAnnotation(XQRpcReference.class);
                    XQRpcReferenceBean referenceBean = new XQRpcReferenceBean(rpcReference.netType(), rpcReference.serializer().getSerializer(), rpcReference.callType(), rpcReference.loadBalance(), iface, rpcReference.version(), rpcReference.timeout(), rpcReference.address(), rpcReference.accessToken(), null, XQRpcSpringInvokerFactory.this.xqRpcInvokerFactory);
                    Object serviceProxy = referenceBean.getObject();
                    field.setAccessible(true);
                    field.set(bean, serviceProxy);
                    XQRpcSpringInvokerFactory.this.log.info("初始化引用类成功. serviceKey = {}, bean.field = {}.{}", new Object[]{XQRpcProviderFactory.makeServiceKey(iface.getName(), rpcReference.version()), beanName, field.getName()});

                    String serviceKey = XQRpcProviderFactory.makeServiceKey(iface.getName(), rpcReference.version());
                    serviceKeyList.add(serviceKey);
                }
            }
        });
        if (this.xqRpcInvokerFactory.getServiceRegistry() != null) {
            try {
                this.xqRpcInvokerFactory.getServiceRegistry().discovery(serviceKeyList);
            } catch (Exception e) {
                this.log.error(e.getMessage(), e);
            }
        }
        return super.postProcessAfterInstantiation(bean, beanName);
    }

    public void destroy()
            throws Exception {
        this.xqRpcInvokerFactory.stop();
    }

    public void setBeanFactory(BeanFactory beanFactory)
            throws BeansException {
        this.beanFactory = beanFactory;
    }
}
